package com.heima.article.service.impl;

import com.alibaba.druid.sql.visitor.functions.If;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.mapper.AuthorMapper;
import com.heima.article.service.ApArticleService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.ArticleInfoDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.pojos.ApAuthor;
import com.heima.model.common.constants.ArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jcodings.ApplyAllCaseFoldFunction;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Connection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
@Transactional
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
    /**
     * 作者表
     *
     * @ap_author
     */
    @Autowired
    AuthorMapper authorMapper;
    /**
     * 文章配置映射器
     *
     * @ap_article_content
     */
    @Autowired
    ApArticleConfigMapper apArticleConfigMapper;
    /**
     * 应用全部配置功能
     *
     * @ap_article_config
     */
    @Autowired
    ApArticleContentMapper apArticleContentMapper;
    /**
     * app文章展示相关方法
     */
    @Autowired
    ApArticleMapper apArticleMapper;
    /**
     * 阿里云前缀
     */
    @Value("${file.oss.web-site}")
    private String webSite;
    /**
     * 最大页面大小
     */
    private static final int MAX_PAGE_SIZE = 50;

    /**
     * 保存app文章
     *
     * @param articleDto
     * @return
     */
    @Override
    public ResponseResult saveArticle(ArticleDto articleDto) {
        //参数检查
        if (articleDto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //1 保存article 文章数据，设置作者id
        //apArticle文章信息表，存储已发布的文章
        ApArticle apArticle = new ApArticle();
        //articleDto就是apArticle的Dto版pojo
        BeanUtils.copyProperties(articleDto, apArticle);
        /**
         * 设置作者的id(ap_article)ApAuthor(ap_author作者表)
         * 根据条件，查询一条记录
         * apAuthor是当前作者的作者表
         * 这里查当前作者信息是因为即将要存的三个表的自媒体文章表里面需要一个字段
         * 这个字段就是文章作者的id
         * */
        ApAuthor apAuthor = authorMapper.selectOne(
                //查询ap_author表
                Wrappers.<ApAuthor>lambdaQuery()
                        //查询条件为作者名称
                        .eq(ApAuthor::getName, apArticle.getAuthorName())
        );

//        ApAuthor apAuthor = authorMapper.selectOne(Wrappers.<ApAuthor>lambdaQuery()
//                .eq(ApAuthor::getName, apArticle.getAuthorName())
//
//        );
        /**
         * TODO 接下来直接cv代码，然后把代码读出来，写好注释，比较菜就尽量写详细一些，包括字段与表的对应关系
         * TODO 都读懂后直接把代码删掉，自己对着刚刚读出来的注释去敲
         * */
        //如果作者查不出来就抛异常
        if (apAuthor == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        /**
         * 给apArticle文章信息表设置作者id
         * 这个作者id是从刚刚查询出来的当前作者的作者表里拿的主键
         * 记得把int转换成long，自媒体已发布文章信息表里的作者id是long型的
         * */
        apArticle.setAuthorId(apAuthor.getId().longValue());


        //TODO 这里开始测试老丁的建议
        /**
         * 判断自媒体文章信息表的dto的主键是否为空
         * 由于保存和修改要做的事情是不一样的
         * 所以用前端传来的articleDto的主键是否为空来判断是否是第一次提交(是需改还是保存)
         * 他如果曾经保存过，那么肯定走过数据库，主键肯定自己生成了，不可能为noll
         * 只有第一次保存的才会没有主键，因为是前端直接发过来的，没有走过数据库
         * */
        if (articleDto.getId() == null) {


            // 普通文章apArticle
            //由于是第一次提交
            // 所以评论数量；阅读数量；收藏数量；点赞数量都是0
            apArticle.setComment(0)
                    .setViews(0)
                    .setCollection(0)
                    .setLikes(0);
            /**
             * 直接保存，保存完会返回雪花算法生成的id
             * 从这里开始，新的文章已经有id了
             * */
            save(apArticle);
            //2 保存ap_article_config 数据(文章配置表)ApArticleConfig
            ApArticleConfig apArticleConfig = new ApArticleConfig();
            //是否转发；是否可评论默认为可以
            //是否下架；是否已删除；默认为否
            apArticleConfig.setIsComment(true)
                    .setIsForward(true)
                    .setIsDown(false)
                    .setIsDelete(false);
            // 文章配置表的文章id存文章信息表的id
            apArticleConfig.setArticleId(apArticle.getId());
            //文章配置映射器插入一条记录
            apArticleConfigMapper.insert(apArticleConfig);
            //3 保存文章article_content 数据创建对象ApArticleContent
            ApArticleContent apArticleContent = new ApArticleContent();
            //ap_article_content表的文章内容从articleDto里拿出来
            apArticleContent
                    .setContent(articleDto.getContent())
                    // ap_article_content表关联文章id是apArticle的上面保存的时候id已经生成了
                    .setArticleId(apArticle.getId());
            //别忘了保存
            apArticleContentMapper.insert(apArticleContent);


        } else { // 修改
            // getById()根据 ID 查询   (ID是ap_article表的字段)
            //获取自媒体文章信息表的id
            //根据前端传来的id查不到东西，他文章没走上面的保存，那么就没有id，这个文章是非法的
            ApArticle byId = getById(articleDto.getId());
            //判断是否能能查到sql
            if (byId == null) {
                //打印日志，ap_article表的id
                log.info("ApArticle getById is null: {}", articleDto.getId());
                //抛出数据不存在
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);

            }
            // 直接去sql修改 apArticle
            updateById(byId);

            // 查出 文章内容apArticleContent的文章id
            //根据id先全查出来根据文章去查contene
            ApArticleContent apArticleContent = apArticleContentMapper.selectById(
                    Wrappers.<ApArticleContent>lambdaQuery()
                            .eq(ApArticleContent::getArticleId, byId)
            );
            //查不出来抛异常，因为只要是保存过的文章，内容表里肯定有记录
            if (apArticleContent == null) {
                log.info("articleContent getById is null: {}", articleDto.getId());
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
            }
            //把前端传来的内容存到文章内容表里
            apArticleContent.setContent(articleDto.getContent());
            //调用修改表的sql去存一下新的文章内容表
            apArticleContentMapper.updateById(apArticleContent);

        }

        //4 返回结果  文章保存后的主键 ID
        return ResponseResult.okResult(apArticle.getId());
    }

    /**
     * 根据参数加载文章列表
     *
     * @param loadtype 1为加载更多  2为加载最新
     * @param dto
     * @return
     */
    @Override
    public ResponseResult load(Short loadtype, ArticleHomeDto dto) {
        //1 给app文章加载策略做参数检查
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 如果频道id为空那么给频道ID设置默认频道
        if (StringUtils.isBlank(dto.getTag())) {
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        // 分页size大小
        Integer size = dto.getSize();
        if (size == null || size <= 0) {
            //无那么默认十
            size = 10;
        }
        /**
         * Math.min(?,?)
         * 返回两个参数中的较小值
         * 这么写就是直接把最大页面大小锁死成50
         * */
        size = Math.min(size, MAX_PAGE_SIZE);
        //放入字段
        dto.setSize(size);
        // 如果前端没传值，那么给这俩值当前时间
        if (dto.getMinBehotTime() == null) {
            //最小时间 （上拉 加载更多）
            dto.setMinBehotTime(new Date());
        }
        if (dto.getMaxBehotTime() == null) {
            //最大时间 (下拉 最新文章)
            dto.setMaxBehotTime(new Date());
        }
        // loadtype加载类型如果为空，也不等于ArticleConstants的两个常量
        if (loadtype == null ||
                !loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE) ||
                !loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW)) {
            //那么默认为加载更多
            loadtype = ArticleConstants.LOADTYPE_LOAD_MORE;
        }
        //2 执行查询，查询文章列表得到自媒体已经发布文章信息表
        /**他这里是在sql里做了判断*/
        List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, loadtype);
        //3 封装结果
        ResponseResult result = ResponseResult.okResult(apArticles);
        //把阿里云那个地址放到请求头里
        result.setHost(webSite);
        //返回结果集
        return result;
    }

    /**
     * 通过文章ID查询文章详情
     * 前端回
     *
     * @param dto 文章信息
     * @return
     */
    @Override
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
        // 1 参数检查dto,文章ID非空校验
        if (dto == null || dto.getArticleId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //获取文章ID
        Long articleId = dto.getArticleId();
        System.out.println("文章ID:"+articleId);
        //2 查询文章配置信息config
        //ApArticleConfig apArticleConfig = apArticleConfigMapper.selectById(articleId);一样的
        ApArticleConfig articleConfig = apArticleConfigMapper.selectOne(Wrappers.<ApArticleConfig>lambdaQuery()
                //根据文章id查出该文章的配置信息
                .eq(ApArticleConfig::getArticleId, articleId));
        //校验配置是否为空
        if (articleConfig == null ||
                //是否下架（true下架）
                articleConfig.getIsDown() ||
                //是否已删除 true: 删除
                articleConfig.getIsDelete()
        ) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章已下架");
        }
        //3 根据文章id查询文章内容
        ApArticleContent articleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery()
                .eq(ApArticleContent::getArticleId, articleId));
        //判断内容非空
        if (articleContent == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }
        //4 把配置和内容放在map里
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("config", articleConfig);
        resultMap.put("content", articleContent);
        //返回封装结果
        return ResponseResult.okResult(resultMap);
    }
}

